package net.lulihu.pangolin.headless.server.kit;

import io.netty.channel.Channel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.lulihu.pangolin.headless.common.util.ReadWriteLockKit;

import java.util.HashMap;
import java.util.Map;

/**
 * 通道映射工具
 * <p>
 * 一个端口只能映射一个通道
 */
@Slf4j
public class ChannelMappingKit {

    private static final ReadWriteLockKit lock = new ReadWriteLockKit();

    // 端口映射信息
    private static final Map<Integer, PortMappingInfo> portMappingInfoMap = new HashMap<>();
    // Id映射端口
    private static final Map<String, Integer> idMappingPortMap = new HashMap<>();


    /**
     * 获取映射端口
     *
     * @param mappingId 映射Id
     * @return 返回映射端口 不存在则为null
     */
    public static Integer getMappingPort(final String mappingId) {
        return lock.exeReadLockMethod(() -> {
            Integer mappingPort;
            if (mappingId != null
                    && (mappingPort = idMappingPortMap.get(mappingId)) != null
                    && portMappingInfoMap.containsKey(mappingPort)) {
                return mappingPort;
            }
            return null;
        });
    }

    /**
     * 判断映射ID是否合法
     *
     * @param mappingId 映射Id
     * @return true合法 反之不合法
     */
    public static boolean isLegal(String mappingId) {
        return lock.exeReadLockMethod(() -> {
            Integer mappingPort = getMappingPort(mappingId);
            return mappingPort != null;
        });
    }

    /**
     * 添加映射
     *
     * @param mappingId 映射id 在网络不稳定断开时重连时使用
     * @param port      代理端口
     * @param address   映射地址
     * @param channel   绑定的通道
     */
    public static void addMapping(final String mappingId, final Integer port, final String address, final Channel channel) {
        lock.exeWriteLockMethod(() -> {
            PortMappingInfo mappingInfo = new PortMappingInfo();
            mappingInfo.setAddress(address);
            mappingInfo.setChannel(channel);
            portMappingInfoMap.put(port, mappingInfo);
            idMappingPortMap.put(mappingId, port);
        });
    }

    /**
     * 获取映射消息
     *
     * @param port 端口
     * @return {@linkplain PortMappingInfo}
     */
    public static PortMappingInfo getMappingInfo(final Integer port) {
        return lock.exeReadLockMethod(() -> portMappingInfoMap.get(port));
    }


    /**
     * 端口映射信息
     */
    @Getter
    @Setter
    @NoArgsConstructor
    public static class PortMappingInfo {

        private Channel channel;

        private String address;

    }

}
